package com.dragons.dragonsvf.config;

import com.dragons.dragonsvf.entities.ModulesInformation;
import com.dragons.dragonsvf.utils.secret.FixedSM4Util;
import io.minio.MinioClient;
import io.minio.ObjectStat;
import io.minio.PutObjectOptions;
import io.minio.Result;
import io.minio.errors.InvalidEndpointException;
import io.minio.errors.InvalidPortException;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.util.UriUtils;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

/**
 * @author Dragons.G
 * @date 2025-05-05 21:21:29
 * @description 动态Minio配置类
 */
@Configuration
@Slf4j
public class DynamicMinioConfig {

    private String bucket;
    private String host;
    private String url;
    private String accessKey;
    private String secretKey;

    private final ModuleConfigService configService;
    private MinioClient minioClient;


    @Autowired
    public DynamicMinioConfig(ModuleConfigService configService) {
        this.configService = configService;
    }

    @PostConstruct
    public void initMinioClient() {
        ModulesInformation m = configService.getMinioConfig();

        String mhost = m.getModule_host_address();
        String port = m.getModule_port();
        String account = m.getModule_account();
        String module_encrypt_pwd = m.getModule_encrypt_pwd();
        String decryptpwd = FixedSM4Util.decryptNoneKeyIvCBC(module_encrypt_pwd);

        log.info("开始初始化 minio ...");
        String host = mhost + ":" + port;
        String url = host + ":" + port + "/budys/";
        String accessKey = account;
        String secretKey = decryptpwd;

        this.host = host;
        this.url = url;
        this.accessKey = accessKey;
        this.secretKey = secretKey;
        this.bucket = "budys";
        log.info("初始化 minio 完成 ...");


        try {
            this.minioClient = new MinioClient(host, accessKey, secretKey);
        } catch (InvalidEndpointException e) {
            e.printStackTrace();
        } catch (InvalidPortException e) {
            e.printStackTrace();
        }
//        this.minioClient = MinioClient.builder()
//            .endpoint(config.getModuleHostAddress() + ":" + config.getModulePort())
//            .credentials(config.getModuleAccount(), secretKey)
//            .build();
    }

    @Bean
    public MinioClient minioClient() {
        return this.minioClient;
    }



    /** 上传 */
    public String putObject(MultipartFile multipartFile) throws Exception {
        // bucket 不存在，创建
        if (!minioClient.bucketExists(this.bucket)) {
            minioClient.makeBucket(this.bucket);
        }
        try (InputStream inputStream = multipartFile.getInputStream()) {
            // 上传文件的名称
            String fileName = multipartFile.getOriginalFilename();
            // PutObjectOptions，上传配置(文件大小，内存中文件分片大小)
            PutObjectOptions putObjectOptions = new PutObjectOptions(multipartFile.getSize(), PutObjectOptions.MIN_MULTIPART_SIZE);
            // 文件的ContentType
            putObjectOptions.setContentType(multipartFile.getContentType());
            minioClient.putObject(this.bucket, fileName, inputStream, putObjectOptions);
            // 返回访问路径
            return this.url + UriUtils.encode(fileName, StandardCharsets.UTF_8);
        }
    }

    /** 上传头像 */
    public String putAvatarObject(MultipartFile multipartFile) throws Exception {
        // bucket 不存在，创建
        if (!minioClient.bucketExists("avatars")) {
            minioClient.makeBucket("avatars");
        }
        makeBucketPublic("avatars");
        try (InputStream inputStream = multipartFile.getInputStream()) {
            // 上传文件的名称
            String fileName = multipartFile.getOriginalFilename();
            // PutObjectOptions，上传配置(文件大小，内存中文件分片大小)
            PutObjectOptions putObjectOptions = new PutObjectOptions(multipartFile.getSize(), PutObjectOptions.MIN_MULTIPART_SIZE);
            // 文件的ContentType
            putObjectOptions.setContentType(multipartFile.getContentType());
            minioClient.putObject("avatars", fileName, inputStream, putObjectOptions);
            // 返回访问路径
            //return this.url + UriUtils.encode(fileName, StandardCharsets.UTF_8);
            return this.host + "/avatars/" + fileName;
        }
    }


    public String putExcelObject(InputStream inputStream, String fileName, String contentType) throws Exception {

        if (!minioClient.bucketExists("excels")) {
            minioClient.makeBucket("excels");
            makeBucketPublic("excels");
        }

        try {
            // 创建上传配置
            PutObjectOptions options = new PutObjectOptions(
                inputStream.available(),  // 注意需要流支持mark/reset
                PutObjectOptions.MIN_MULTIPART_SIZE
            );
            options.setContentType(contentType);

            // 使用带MD5校验的上传方式（网页10最佳实践）
            minioClient.putObject("excels", fileName, inputStream, options);

            // 返回带签名的访问地址（网页9方案）
            return minioClient.presignedGetObject("excels", fileName);
        } finally {
            inputStream.close();  // 确保流关闭
        }
    }

    /** 上传图片 */
    public String uploadImgObject(MultipartFile multipartFile) throws Exception {
        // bucket 不存在，创建
        if (!minioClient.bucketExists("reminiscences")) {
            minioClient.makeBucket("reminiscences");
        }
        makeBucketPublic("reminiscences");
        try (InputStream inputStream = multipartFile.getInputStream()) {
            // 上传文件的名称
            String fileName = multipartFile.getOriginalFilename();
            // PutObjectOptions，上传配置(文件大小，内存中文件分片大小)
            PutObjectOptions putObjectOptions = new PutObjectOptions(multipartFile.getSize(), PutObjectOptions.MIN_MULTIPART_SIZE);
            // 文件的ContentType
            putObjectOptions.setContentType(multipartFile.getContentType());
            minioClient.putObject("reminiscences", fileName, inputStream, putObjectOptions);
            // 返回访问路径
            //return this.url + UriUtils.encode(fileName, StandardCharsets.UTF_8);
            return this.host + "/reminiscences/" + fileName;
        }
    }


    /** 文件下载 */
    public void download(String fileName, HttpServletResponse response){
        // 从链接中得到文件名
        InputStream inputStream;
        try {
            MinioClient minioClient = new MinioClient(host, accessKey, secretKey);
            ObjectStat stat = minioClient.statObject(bucket, fileName);
            inputStream = minioClient.getObject(bucket, fileName);
            response.setContentType(stat.contentType());
            response.setCharacterEncoding("UTF-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
            IOUtils.copy(inputStream, response.getOutputStream());
            inputStream.close();
        } catch (Exception e){
            e.printStackTrace();
            System.out.println("有异常：" + e);
        }
    }

    /** 列出所有存储桶名称 */
    public List<String> listBucketNames() throws Exception {
        List<Bucket> bucketList = listBuckets();
        List<String> bucketListName = new ArrayList<>();
        for (Bucket bucket : bucketList) {
            bucketListName.add(bucket.name());
        }
        return bucketListName;
    }

    /** 查看所有桶 */
    public List<Bucket> listBuckets() throws Exception {
        return minioClient.listBuckets();
    }

    /** 检查存储桶是否存在 */
    public boolean bucketExists(String bucketName) throws Exception {
        boolean flag = minioClient.bucketExists(bucketName);
        if (flag) {
            return true;
        }
        return false;
    }

    /** 创建存储桶 */
    public boolean makeBucket(String bucketName)
        throws Exception {
        boolean flag = bucketExists(bucketName);
        if (!flag) {
            minioClient.makeBucket(bucketName);
            return true;
        } else {
            return false;
        }
    }

    /** 删除桶 @param bucketName */
    public boolean removeBucket(String bucketName)
        throws Exception {
        boolean flag = bucketExists(bucketName);
        if (flag) {
            Iterable<Result<Item>> myObjects = listObjects(bucketName);
            for (Result<Item> result : myObjects) {
                Item item = result.get();
                // 有对象文件，则删除失败
                if (item.size() > 0) {
                    return false;
                }
            }
            // 删除存储桶，注意，只有存储桶为空时才能删除成功。
            minioClient.removeBucket(bucketName);
            flag = bucketExists(bucketName);
            if (!flag) {
                return true;
            }

        }
        return false;
    }

    /** 列出存储桶中的所有对象 bucketName 存储桶名称 */
    public Iterable<Result<Item>> listObjects(String bucketName) throws Exception {
        boolean flag = bucketExists(bucketName);
        if (flag) {
            return minioClient.listObjects(bucketName);
        }
        return null;
    }

    /** 列出存储桶中的所有对象名称 bucketName 存储桶名称 */
    public List<String> listObjectNames(String bucketName) throws Exception {
        List<String> listObjectNames = new ArrayList<>();
        boolean flag = bucketExists(bucketName);
        if (flag) {
            Iterable<Result<Item>> myObjects = listObjects(bucketName);
            for (Result<Item> result : myObjects) {
                Item item = result.get();
                listObjectNames.add(item.objectName());
            }
        }
        return listObjectNames;
    }

    /** 删除一个对象 bucketName 存储桶名称 objectName 存储桶里的对象名称 */
    public boolean removeObject(String bucketName, String objectName) throws Exception {
        boolean flag = bucketExists(bucketName);
        if (flag) {
            List<String> objectList = listObjectNames(bucketName);
            for (String s : objectList) {
                if(s.equals(objectName)){
                    minioClient.removeObject(bucketName, objectName);
                    return true;
                }
            }
        }
        return false;
    }

    /** 文件访问路径 bucketName 存储桶名称 objectName 存储桶里的对象名称 */
    public String getObjectUrl(String bucketName, String objectName) throws Exception {
        boolean flag = bucketExists(bucketName);
        String url = "";
        if (flag) {
            url = minioClient.getObjectUrl(bucketName, objectName);
        }
        return url;
    }

    /**
     * 创建存储桶并设置自定义权限策略（Java 1.8兼容版）
     * @param bucketName 存储桶名称
     * @param policyJson 权限策略JSON字符串（需符合MinIO策略格式）
     * @return 是否创建成功（true=创建成功，false=已存在）
     * @throws Exception 策略格式错误或权限不足时抛出异常
     */
    public boolean makeBucketWithPolicy(String bucketName, String policyJson) throws Exception {
        if (!minioClient.bucketExists(bucketName)) {
            minioClient.makeBucket(bucketName);
            String formattedPolicy = policyJson.replace("bucket-name", bucketName);
            minioClient.setBucketPolicy(bucketName, formattedPolicy); // 网页3/7的策略设置方式
            return true;
        }
        return false;
    }

    /**
     * 设置存储桶为完全开放策略（Java 1.8兼容版）
     * @param bucketName 存储桶名称
     * @throws Exception 策略设置失败时抛出异常
     */
    public void makeBucketPublic(String bucketName) throws Exception {
        String publicPolicy = "{"
            + "\"Version\":\"2012-10-17\","
            + "\"Statement\":[{"
            + "\"Effect\":\"Allow\","
            + "\"Principal\":{\"AWS\":[\"*\"]},"
            + "\"Action\":["
            + "\"s3:GetObject\","
            + "\"s3:PutObject\","
            + "\"s3:DeleteObject\","
            + "\"s3:ListBucket\""
            + "],"
            + "\"Resource\":["
            + "\"arn:aws:s3:::" + bucketName + "/*\","
            + "\"arn:aws:s3:::" + bucketName + "\""
            + "]}]}"; // 网页5的权限配置规范

        if (!bucketExists(bucketName)) {
            makeBucketWithPolicy(bucketName, publicPolicy);
        } else {
            minioClient.setBucketPolicy(bucketName, publicPolicy);
        }
    }

    /**
     * 通用权限操作方法（Java 1.8兼容版）
     * @param bucketName 存储桶名称
     * @param policyType 权限类型枚举（READ_ONLY/WRITE_ONLY/FULL_CONTROL）
     * @throws Exception 策略设置失败时抛出异常
     */
    public void setBucketAccessPolicy(String bucketName, PolicyType policyType) throws Exception {
        String policy;
        switch (policyType) {
            case READ_ONLY:
                policy = "{"
                    + "\"Version\":\"2012-10-17\","
                    + "\"Statement\":[{"
                    + "\"Effect\":\"Allow\","
                    + "\"Principal\":\"*\","
                    + "\"Action\":[\"s3:GetObject\"],"
                    + "\"Resource\":[\"arn:aws:s3:::" + bucketName + "/*\"]"
                    + "}]}"; // 网页5的只读策略示例
                break;
            case WRITE_ONLY:
                policy = "{"
                    + "\"Version\":\"2012-10-17\","
                    + "\"Statement\":[{"
                    + "\"Effect\":\"Allow\","
                    + "\"Principal\":\"*\","
                    + "\"Action\":[\"s3:PutObject\"],"
                    + "\"Resource\":[\"arn:aws:s3:::" + bucketName + "/*\"]"
                    + "}]}";
                break;
            case FULL_CONTROL:
                policy = "{"
                    + "\"Version\":\"2012-10-17\","
                    + "\"Statement\":[{"
                    + "\"Effect\":\"Allow\","
                    + "\"Principal\":\"*\","
                    + "\"Action\":[\"s3:*\"],"
                    + "\"Resource\":["
                    + "\"arn:aws:s3:::" + bucketName + "\","
                    + "\"arn:aws:s3:::" + bucketName + "/*\""
                    + "]}]}"; // 网页5的全控制策略优化
                break;
            default:
                throw new IllegalArgumentException("Invalid policy type");
        }
        minioClient.setBucketPolicy(bucketName, policy);
    }

    // 权限类型枚举定义
    public enum PolicyType {
        READ_ONLY, WRITE_ONLY, FULL_CONTROL
    }
}
